#include <assert.h>

#include "pyparser.h"
#include "parsetok.h" // For PyParser_ParseStringFlags
#include "ast.h" // For PyAST_FromNode

#define DEBUG( format, args... ) printf( format, ## args )
//~ #define DEBUG( format, args... )

extern grammar _PyParser_Grammar; /* From graminit.c */

/** Returns an abstract syntax tree representing the specified string. The module must be freed by using mod_free().
* @param string the Python source code.
* @param filename the file the source came from. If it is NULL, the string is parsed in the "Py_string_input" context.
*/
mod_ty PyASTFromString( const char* string, const char* filename )
{
	assert( string != NULL );
		
	// This parameter must be either Py_file_input, Py_eval_input or Py_single_input
	int start = Py_file_input;
	if ( filename == NULL )
	{
		filename = "<string>";
	}
	
	// Parse the source code
	PyCompilerFlags flags = { 0 };
	perrdetail err;
	node* n = PyParser_ParseStringFlagsFilename(string, filename, &_PyParser_Grammar,
		start, &err, PARSER_FLAGS(&flags) );
	
	if ( n == NULL )
	{
		// TODO: Handle errors
		return NULL;
	}
	assert( n != NULL );
	
	// Build an abstract syntax tree from the parse tree
	mod_ty mod = PyAST_FromNode(n, &flags, filename);
	PyNode_Free(n);
	n = NULL;
	
	if ( mod == NULL )
	{
		// TODO: Handle errors
		return NULL;
	}
	
	return mod;
}

/** Returns an abstract syntax tree representing the specified FILE object. The module must be freed by using mod_free().
* @param handle the file handle containing Python source code.
* @param filename the file the source came from. If it is NULL, the string is parsed in the "Py_string_input" context.
*/
mod_ty PyASTFromFile( FILE* handle, const char* filename )
{
	assert( handle != NULL );
	assert( filename != NULL );
	
	// Parse the source code
	PyCompilerFlags flags = { 0 };
	perrdetail err;
	node* n = PyParser_ParseFileFlags( handle, filename, &_PyParser_Grammar,
		Py_file_input, NULL, NULL, &err, PARSER_FLAGS(&flags) );
	
	if ( n == NULL )
	{
		// TODO: Handle errors
		return NULL;
	}
	assert( n != NULL );
	
	// Build an abstract syntax tree from the parse tree
	mod_ty mod = PyAST_FromNode(n, &flags, filename);
	PyNode_Free(n);
	n = NULL;
	
	if ( mod == NULL )
	{
		// TODO: Handle errors
		return NULL;
	}
	
	return mod;
}


/**
Grabs the arguments out of the statement and calls the appropriate callback.
@param visitState passed back as the first parameter to each callback.
@param callbacks the callbacks for each possible type of statement.
@param statement the statement to visit.
*/
void PyParse_Statement( void* visitState, const struct PyParse_Callbacks* callbacks, stmt_ty statement )
{
	assert( callbacks != NULL );
	assert( statement != NULL );
		
	switch ( statement->kind )
	{
		case Assign_kind:
			if ( callbacks->assign != NULL )
			{
				callbacks->assign( visitState, statement->v.Assign.targets, statement->v.Assign.value );
			}
			break;
		
		case Return_kind:
			if ( callbacks->callback_return != NULL )
			{
				callbacks->callback_return( visitState, statement->v.Return.value );
			}
			break;
		
		case Print_kind:
			if ( callbacks->print != NULL )
			{
				callbacks->print( visitState, statement->v.Print.dest, statement->v.Print.values, statement->v.Print.nl );
			}
			break;
		
		case FunctionDef_kind:
			if ( callbacks->function != NULL )
			{
				callbacks->function( visitState, statement->v.FunctionDef.name, statement->v.FunctionDef.args, statement->v.FunctionDef.body, statement->v.FunctionDef.decorators );
			}
			break;

		case Expr_kind:
			if ( callbacks->expression != NULL )
			{
				callbacks->expression( visitState, statement->v.Expr.value );
			}
			break;
		
		default:
			DEBUG( "unknown statement kind = %d\n", statement->kind );
			abort();
	}
}

/**
Iterates through the collection of statements, calling the specified callbacks for each one.
@param visitState passed back as the first parameter to each callback.
@param callbacks the callbacks for each possible type of statement.
*/
void PyParse_Statements( void* visitState, const struct PyParse_Callbacks* callbacks, asdl_seq* statements )
{
	assert( callbacks != NULL );
	assert( statements != NULL );
	assert( statements->size > 0 );
	
	/* Iterate over all statements in the sequence. */
	for ( int i = 0; i < statements->size; ++ i )
	{
		PyParse_Statement( visitState, callbacks, statements->elements[i] );
	}
}


void PyParse_Expression( void* visitState, const struct PyParse_Callbacks* callbacks, expr_ty expression )
{
	assert( callbacks != NULL );
	assert( expression != NULL );
	
	switch ( expression->kind )
	{
		case Name_kind:
			if ( callbacks->name != NULL )
			{
				callbacks->name( visitState, expression->v.Name.id, expression->v.Name.ctx );
			}
			break;
		
		case Num_kind:
			if ( callbacks->number != NULL )
			{
				callbacks->number( visitState, expression->v.Num.n );
			}
			break;
		
		case Str_kind:
			if ( callbacks->string != NULL )
			{
				callbacks->string( visitState, expression->v.Str.s );
			}
			break;
		
		case Call_kind:
			if ( callbacks->call != NULL )
			{
				callbacks->call( visitState, expression->v.Call.func, expression->v.Call.args, expression->v.Call.keywords, expression->v.Call.starargs, expression->v.Call.kwargs );
			}
			break;
		
		default:
			DEBUG( "unknown expression kind = %d\n", expression->kind );
			abort();
	}
}

